/**
 * Copyright (c) 2006-2015, JGraph Ltd
 * Copyright (c) 2006-2015, Gaudenz Alder
 */
mxCodecRegistry.register(function()
{
    /**
     * Class: mxCellCodec
     *
     * Codec for <mxCell>s. This class is created and registered
     * dynamically at load time and used implicitely via <mxCodec>
     * and the <mxCodecRegistry>.
     *
     * Transient Fields:
     *
     * - children
     * - edges
     * - overlays
     * - mxTransient
     *
     * Reference Fields:
     *
     * - parent
     * - source
     * - target
     *
     * Transient fields can be added using the following code:
     *
     * mxCodecRegistry.getCodec(mxCell).exclude.push('name_of_field');
     *
     * To subclass <mxCell>, replace the template and add an alias as
     * follows.
     *
     * (code)
     * function CustomCell(value, geometry, style)
     * {
     *   mxCell.apply(this, arguments);
     * }
     *
     * mxUtils.extend(CustomCell, mxCell);
     *
     * mxCodecRegistry.getCodec(mxCell).template = new CustomCell();
     * mxCodecRegistry.addAlias('CustomCell', 'mxCell');
     * (end)
     */
    var codec = new mxObjectCodec(new mxCell(),
        ['children', 'edges', 'overlays', 'mxTransient'],
        ['parent', 'source', 'target']);

    /**
     * Function: isCellCodec
     *
     * Returns true since this is a cell codec.
     */
    codec.isCellCodec = function()
    {
        return true;
    };

    /**
     * Overidden to disable conversion of value to number.
     */
    codec.isNumericAttribute = function(dec, attr, obj)
    {
        return attr.nodeName !== 'value' && mxObjectCodec.prototype.isNumericAttribute.apply(this, arguments);
    };

    /**
     * Function: isExcluded
     *
     * Excludes user objects that are XML nodes.
     */
    codec.isExcluded = function(obj, attr, value, isWrite)
    {
        return mxObjectCodec.prototype.isExcluded.apply(this, arguments) ||
            (isWrite && attr == 'value' &&
                value.nodeType == mxConstants.NODETYPE_ELEMENT);
    };

    /**
     * Function: afterEncode
     *
     * Encodes an <mxCell> and wraps the XML up inside the
     * XML of the user object (inversion).
     */
    codec.afterEncode = function(enc, obj, node)
    {
        if (obj.value != null && obj.value.nodeType == mxConstants.NODETYPE_ELEMENT)
        {
            // Wraps the graphical annotation up in the user object (inversion)
            // by putting the result of the default encoding into a clone of the
            // user object (node type 1) and returning this cloned user object.
            var tmp = node;
            node = mxUtils.importNode(enc.document, obj.value, true);
            node.appendChild(tmp);

            // Moves the id attribute to the outermost XML node, namely the
            // node which denotes the object boundaries in the file.
            var id = tmp.getAttribute('id');
            node.setAttribute('id', id);
            tmp.removeAttribute('id');
        }

        return node;
    };

    /**
     * Function: beforeDecode
     *
     * Decodes an <mxCell> and uses the enclosing XML node as
     * the user object for the cell (inversion).
     */
    codec.beforeDecode = function(dec, node, obj)
    {
        var inner = node.cloneNode(true);
        var classname = this.getName();

        if (node.nodeName != classname)
        {
            // Passes the inner graphical annotation node to the
            // object codec for further processing of the cell.
            var tmp = node.getElementsByTagName(classname)[0];

            if (tmp != null && tmp.parentNode == node)
            {
                mxUtils.removeWhitespace(tmp, true);
                mxUtils.removeWhitespace(tmp, false);
                tmp.parentNode.removeChild(tmp);
                inner = tmp;
            }
            else
            {
                inner = null;
            }

            // Creates the user object out of the XML node
            obj.value = node.cloneNode(true);
            var id = obj.value.getAttribute('id');

            if (id != null)
            {
                obj.setId(id);
                obj.value.removeAttribute('id');
            }
        }
        else
        {
            // Uses ID from XML file as ID for cell in model
            obj.setId(node.getAttribute('id'));
        }

        // Preprocesses and removes all Id-references in order to use the
        // correct encoder (this) for the known references to cells (all).
        if (inner != null)
        {
            for (var i = 0; i < this.idrefs.length; i++)
            {
                var attr = this.idrefs[i];
                var ref = inner.getAttribute(attr);

                if (ref != null)
                {
                    inner.removeAttribute(attr);
                    var object = dec.objects[ref] || dec.lookup(ref);

                    if (object == null)
                    {
                        // Needs to decode forward reference
                        var element = dec.getElementById(ref);

                        if (element != null)
                        {
                            var decoder = mxCodecRegistry.codecs[element.nodeName] || this;
                            object = decoder.decode(dec, element);
                        }
                    }

                    obj[attr] = object;
                }
            }
        }

        return inner;
    };

    // Returns the codec into the registry
    return codec;

}());
